package com.sharding4.multi.mybatis.seata.config;

import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
import com.github.pagehelper.PageHelper;
import com.sharding4.multi.mybatis.seata.algorithm.DatabasePreciseShardingAlgorithm;
import com.sharding4.multi.mybatis.seata.algorithm.OrderTablePreciseShardingAlgorithm;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.shardingsphere.api.config.sharding.ShardingRuleConfiguration;
import org.apache.shardingsphere.api.config.sharding.TableRuleConfiguration;
import org.apache.shardingsphere.api.config.sharding.strategy.StandardShardingStrategyConfiguration;
import org.apache.shardingsphere.shardingjdbc.api.ShardingDataSourceFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;

import javax.sql.DataSource;
import java.sql.SQLException;
import java.util.*;

/**
 * @author lenovo
 */
@Slf4j
@Configuration
@MapperScan(basePackages = "com.sharding4.multi.mybatis.seata.mapper.order", sqlSessionFactoryRef = "shardingSqlSessionFactory")
public class OrderDbConfig {
    @Bean(name = "saleorder01")
    @ConfigurationProperties(prefix = "spring.datasource.druid.saleorder01")
    public DataSource saleorder01() {
        return DruidDataSourceBuilder.create().build();
    }

    @Bean(name = "saleorder02")
    @ConfigurationProperties(prefix = "spring.datasource.druid.saleorder02")
    public DataSource saleorder02() {
        return DruidDataSourceBuilder.create().build();
    }

    @Bean("dataSource")
    public DataSource dataSource(@Qualifier("saleorder01") DataSource saleorder01, @Qualifier("saleorder02") DataSource saleorder02) throws SQLException {
        log.info("初始化 sharding 数据源");
        // 配置真实数据源
        Map<String, DataSource> dataSourceMap = new HashMap<>(2);
        dataSourceMap.put("saleorder01", saleorder01);
        dataSourceMap.put("saleorder02", saleorder02);
        ShardingRuleConfiguration shardingRuleConfig = new ShardingRuleConfiguration();
        // 如果有多个数据表需要分表，依次添加到这里
        shardingRuleConfig.getTableRuleConfigs().add(this.getOrderTableRuleConfiguration());
        // shardingRuleConfig.getTableRuleConfigs().add(this.getOrderItemTableRuleConfiguration());
        Properties p = new Properties();
        p.setProperty("sql.show", Boolean.TRUE.toString());
        // 获取数据源对象
        DataSource dataSource = ShardingDataSourceFactory.createDataSource(dataSourceMap, shardingRuleConfig, p);
        return dataSource;
    }

    @Bean(name = "shardingSqlSessionFactory")
    public SqlSessionFactory shardingSqlSessionFactory(@Qualifier("dataSource") DataSource dataSource) throws Exception {
        // 使用 MyBatis 时
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        // 使用 MyBatis-Plus 时
        // MybatisSqlSessionFactoryBean bean = new MybatisSqlSessionFactoryBean();
        bean.setDataSource(dataSource);
        bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/order/*.xml"));
        return bean.getObject();
    }

    @Bean("shardingTransactionManger")
    public DataSourceTransactionManager shardingTransactionManger(@Qualifier("dataSource") DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }

    @Bean(name = "shardingSqlSessionTemplate")
    public SqlSessionTemplate shardingSqlSessionTemplate(@Qualifier("shardingSqlSessionFactory") SqlSessionFactory sqlSessionFactory) {
        return new SqlSessionTemplate(sqlSessionFactory);
    }

    /**
     * 订单表的分表规则配置
     */
    private TableRuleConfiguration getOrderTableRuleConfiguration() {
        TableRuleConfiguration result = new TableRuleConfiguration("t_order", "saleorder01.t_order_$->{1..2},saleorder02.t_order_$->{3..4}");
        // result.setDatabaseShardingStrategyConfig(this.getDatabaseStrategyConfiguration());
        result.setDatabaseShardingStrategyConfig(new StandardShardingStrategyConfiguration("orderId", new DatabasePreciseShardingAlgorithm()));
        // result.setTableShardingStrategyConfig(this.getStrategyConfiguration());
        result.setTableShardingStrategyConfig(new StandardShardingStrategyConfiguration("orderId", new OrderTablePreciseShardingAlgorithm()));
        return result;
    }

    @Bean(name = "pageHelper")
    public PageHelper pageHelper() {
        PageHelper pageHelper = new PageHelper();
        Properties properties = new Properties();
        properties.setProperty("reasonable", "true");
        properties.setProperty("supportMethodsArguments", "true");
        properties.setProperty("returnPageInfo", "true");
        properties.setProperty("params", "count=countSql");
        pageHelper.setProperties(properties);
        return pageHelper;
    }
}